from abc import ABC, abstractmethod
import struct

from common import Logger

Log = Logger()

# #################### PULL ###################### #
TX_FRAME_HEAD = 0x7b
TX_FRAME_END = 0x7a
TX_CMD_PID = 0x01
####################################################
# ################### PUSH ####################### #
RX_FRAME_HEAD = 0x7a
RX_FRAME_END = 0x7b
RX_CMD_CHN = 0x01
####################################################

# recv data buf
__rx_buf = []


def __check_code(cmd, data: []) -> bytes:
    sum = cmd
    sum += len(data)
    for d in data:
        sum += int(d)
    # 取高位
    sum &= 0xFF00
    sum >>= 8
    sum &= 0xFF
    return sum


def _make_request(cmd, data: []) -> bytes:
    # 消息组成
    #   帧头  |   命令  |   数据长度    |   数据  |   校验码 | 帧尾
    #   1字节 |   1字节 |   1字节      |  n字节  |  1字节   | 1字节
    # 命令: 请求类型的标识
    # 数据长度: 表示后面 数据 的字节个数
    # 校验码:  命令 + 数据长度 + 数据, 取高位
    result = b''
    result += struct.pack('B', TX_FRAME_HEAD)
    result += struct.pack('B', cmd)
    result += struct.pack('B', len(data))
    for d in data:
        result += struct.pack('B', d)
    result += struct.pack('B', __check_code(cmd, data))
    result += struct.pack('B', TX_FRAME_END)
    Log.debug("send: {}".format(list(result)))
    return result


def create_virtual_data(values: list) -> list:
    arr = []
    data_len = len(values) * 4

    sum = RX_CMD_CHN
    sum += data_len

    arr.append(RX_FRAME_HEAD)
    arr.append(RX_CMD_CHN)
    arr.append(data_len)

    for item in values:
        data = list(struct.pack('f', item))
        arr.extend(data)
        for i in data:
            sum += i

    sum &= 0xFF00
    sum >>= 8
    sum &= 0xFF

    arr.append(sum)
    arr.append(RX_FRAME_END)

    return arr


class Request(ABC):
    @abstractmethod
    def get_data(self) -> bytes:
        pass


class ConfigPIDRequest(Request):
    def __init__(self, idx: int, kp: float = None, ki: float = None, kd: float = None):
        self.__idx: int = idx
        self.__kp: float = kp
        self.__ki: float = ki
        self.__kd: float = kd

    def get_data(self) -> bytes:
        global TX_CMD_PID
        Log.debug('cmd: {}'.format(TX_CMD_PID))
        cmd = TX_CMD_PID
        data = [self.__idx]
        #  帧头  |   命令  |   数据长度    |   数据  |   校验码 | 帧尾
        #  1字节 |   1字节 |   1字节      |  n字节  |  1字节   | 1字节
        # 数据: 4个字节，表示float
        data.extend(list(struct.pack('f', self.__kp)))
        data.extend(list(struct.pack('f', self.__ki)))
        data.extend(list(struct.pack('f', self.__kd)))
        return _make_request(cmd, data)


class Response(ABC):
    @abstractmethod
    def set_data(self, arr) -> bytes:
        pass


class HelloResponse(Response):
    def set_data(self, arr) -> bytes:
        pass


class ChannelResponse(Response):
    def __init__(self):
        self.values = []

    def set_data(self, arr) -> bytes:
        #   帧头  |   命令  |   数据长度    |   数据  |   校验码 | 帧尾
        #   1字节 |   1字节 |   1字节      |  n字节  |  1字节   | 1字节
        # 数据长度:
        # 数据:
        #  data  | data | data |
        #   4字节 | ............|
        ###########################################################
        c = int(arr[2] / 4)
        for i in range(c):
            b = b''
            b += struct.pack('B', arr[3 + i * 4 + 0])
            b += struct.pack('B', arr[3 + i * 4 + 1])
            b += struct.pack('B', arr[3 + i * 4 + 2])
            b += struct.pack('B', arr[3 + i * 4 + 3])
            self.values.append(struct.unpack('f', b)[0])

    def get_values(self):
        return self.values


def __make_response(arr) -> Response:
    resp: Response = None
    # 判断命令
    cmd = arr[1]
    if cmd == 0x01:
        resp = ChannelResponse()
        resp.set_data(arr)
    # # TODO: other response
    return resp


def __parse_response(msg):
    global __rx_buf
    if msg is not None:
        __rx_buf.append(msg)
    # 消息组成
    #   帧头  |   命令  |   数据长度    |   数据  |   校验码 | 帧尾
    #   1字节 |   1字节 |   1字节      |  n字节  |  1字节   | 1字节
    # 命令: 请求类型的标识
    # 数据长度: 表示后面 数据 的字节个数
    # 校验码:  命令 + 数据长度 + 数据, 取高位
    if len(__rx_buf) < 5:
        return None
    # 判断是不是帧头
    if __rx_buf[0] != RX_FRAME_HEAD:
        # 丢弃第一个
        __rx_buf.pop(0)
        return __parse_response(None)
    # 获取命令
    cmd = __rx_buf[1]
    # 获取数据长度
    # data_len = int.from_bytes(__rx_buf[2], byteorder='big')
    data_len = __rx_buf[2]
    # 不够一条指令
    if len(__rx_buf) < data_len + 5:
        return None
    sum = 0
    for i in range(1, data_len + 3):
        # sum += int.from_bytes(__rx_buf[i], byteorder='big')
        sum += __rx_buf[i]
    sum &= 0xFF00
    sum >>= 8
    sum &= 0xFF
    # 校验验证码
    # if sum != int.from_bytes(__rx_buf[data_len + 3], byteorder='big'):
    if sum != __rx_buf[data_len + 3]:
        # 丢弃第一个
        __rx_buf.pop(0)
        return __parse_response(None)
    # 校验帧尾
    if __rx_buf[data_len + 4] != RX_FRAME_END:
        # 丢弃第一个
        __rx_buf.pop(0)
        return __parse_response(None)
    # 全部通过
    # pop 所有
    arr = []
    for i in range(0, data_len + 5):
        arr.append(__rx_buf.pop(0))
    return __make_response(arr)

def Get_Response(msg) -> Response:
    return __parse_response(int.from_bytes(msg, byteorder='big'))

# def Get_Response(data) -> Response:
#     msg = []
#     for i in range(len(data)):
#         msg.append(int.from_bytes([data[i]], byteorder='big'))
#     return __parse_response(msg)
